package cn.wlinker.video.monitor.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONObject;
import cn.wlinker.video.monitor.config.DefaultConfig;
import cn.wlinker.video.monitor.domain.Page;
import cn.wlinker.video.monitor.domain.PageQuery;
import cn.wlinker.video.monitor.domain.VideoBaseEntity;
import cn.wlinker.video.monitor.domain.dto.*;
import cn.wlinker.video.monitor.domain.vo.*;
import cn.wlinker.video.monitor.utils.*;
import cn.wlinker.video.monitor.service.ICommonVideoService;
import cn.wlinker.video.monitor.service.IVideoBrandEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;

import java.util.*;

/**
 * 通用的视频的服务
 *
 * @author gxsjx
 * @version 1.0
 * @date 2022/7/26
 */
@Slf4j
public class CommonVideoServiceImpl implements ICommonVideoService {

    private CommonVideoServiceImpl() {
    }

    public static final CommonVideoServiceImpl INSTANCE = new CommonVideoServiceImpl();

    @Override
    public VideoVO getRelayUrl(PlayUrlDTO playUrlDTO) {
        Assert.notNull(playUrlDTO.getStartTime(), "开始时间不能为空");
        Assert.notNull(playUrlDTO.getStopTime(), "结束时间不能为空");
        MonitorDeviceDTO monitorDeviceDtoById = VideoKeyUtils.getMonitorDeviceDtoById(playUrlDTO.getId());
        return IVideoBrandEnum.getVideoStream(monitorDeviceDtoById)
                .getRelayUrl(monitorDeviceDtoById, playUrlDTO);
    }


    @Override
    public JSONObject capacity(String id) {
        MonitorDeviceDTO monitorDeviceDtoById = VideoKeyUtils.getMonitorDeviceDtoById(id);
        return IVideoBrandEnum.getVideoStream(monitorDeviceDtoById)
                .capacity(monitorDeviceDtoById);
    }

    /**
     * 添加预置点
     *
     * @param presetDTO
     * @return
     */
    @Override
    public PresetAddVO presetAdd(PresetDTO presetDTO) {
        MonitorDeviceDTO monitorDeviceDtoById = VideoKeyUtils.getMonitorDeviceDtoById(presetDTO.getId());
        return IVideoBrandEnum.getVideoStream(monitorDeviceDtoById)
                .presetAdd(monitorDeviceDtoById, presetDTO.getIndex());
    }

    /**
     * 调用预置点
     *
     * @param presetDTO
     * @return
     */
    @Override
    public Boolean presetMove(PresetDTO presetDTO) {
        MonitorDeviceDTO monitorDeviceDtoById = VideoKeyUtils.getMonitorDeviceDtoById(presetDTO.getId());
        return IVideoBrandEnum.getVideoStream(monitorDeviceDtoById)
                .presetMove(monitorDeviceDtoById, presetDTO.getIndex());
    }

    /**
     * 清除预置点
     *
     * @param presetDTO
     * @return
     */
    @Override
    public Boolean presetClear(PresetDTO presetDTO) {
        MonitorDeviceDTO monitorDeviceDtoById = VideoKeyUtils.getMonitorDeviceDtoById(presetDTO.getId());
        return IVideoBrandEnum.getVideoStream(monitorDeviceDtoById)
                .presetClear(monitorDeviceDtoById, presetDTO.getIndex());
    }

    /**
     * 图片抓拍
     *
     * @param captureDTO
     * @return
     */
    @Override
    public PictureUrlVO capture(CaptureDTO captureDTO) {
        MonitorDeviceDTO monitorDeviceDtoById = VideoKeyUtils.getMonitorDeviceDtoById(captureDTO.getId());
        return IVideoBrandEnum.getVideoStream(monitorDeviceDtoById)
                .capture(monitorDeviceDtoById, captureDTO.getQuality());
    }

    /**
     * 开始云台转动
     *
     * @param ptzControlDTO
     * @return
     */
    @Override
    public Boolean ptzStart(PtzControlDTO ptzControlDTO) {
        MonitorDeviceDTO monitorDeviceDtoById = VideoKeyUtils.getMonitorDeviceDtoById(ptzControlDTO.getId());
        return IVideoBrandEnum.getVideoStream(monitorDeviceDtoById)
                .ptzStart(monitorDeviceDtoById, ptzControlDTO.getDirection(), ptzControlDTO.getSpeed());
    }

    /**
     * 停止云台转动
     *
     * @param ptzControlDTO
     * @return
     */
    @Override
    public Boolean ptzStop(PtzControlDTO ptzControlDTO) {
        MonitorDeviceDTO monitorDeviceDtoById = VideoKeyUtils.getMonitorDeviceDtoById(ptzControlDTO.getId());
        return IVideoBrandEnum.getVideoStream(monitorDeviceDtoById)
                .ptzStop(monitorDeviceDtoById, ptzControlDTO.getDirection());
    }

    /**
     * 根据id获取视频流
     *
     * @param playUrlDTO
     * @return
     */
    @Override
    public VideoVO getUrl(PlayUrlDTO playUrlDTO) {
        VideoConfigUtils.initPlayUrlDTO(playUrlDTO);
        String id = playUrlDTO.getId();
        VideoBaseEntity videoBaseEntity = MonitorDeviceCacheUtils.get(id);
        if (ObjectUtil.isEmpty(videoBaseEntity)) {
            return VideoUtils.getVideoByMonitorType(id, playUrlDTO);
        }
        MonitorDeviceDTO monitorDeviceDTO = BeanUtil.copyProperties(videoBaseEntity, MonitorDeviceDTO.class);
        return VideoUtils.getVideoByDto(monitorDeviceDTO, playUrlDTO);
    }

    /**
     * 根据id列表获取视频流
     *
     * @param ids id列表
     * @return
     */
    @Override
    public List<VideoVO> getUrls(List<String> ids, PlayUrlDTO playUrlDTO) {
        //做并发的处理，同时处理id获取播放流，关键返回后存储的顺序要一致。
        BuildStreamThreadPoolUtils<VideoVO> pool = new BuildStreamThreadPoolUtils<>();
        for (String id : ids) {
            playUrlDTO.setId(id);
            pool.submit(() -> {
                log.info("并发处理，获取播放流 -- >{}", id);
                return this.getUrl(playUrlDTO);
            });
        }
        return pool.get();
    }

    @Override
    public Page<MonitorDeviceVO> getMonitorDeviceList(MonitorDeviceDTO monitorDeviceDTO, PageQuery pageQuery) {
        String monitorType = monitorDeviceDTO.getMonitorType();
        String accessPlatform = monitorDeviceDTO.getAccessPlatform();
        if (StrUtil.isBlank(monitorType) || StrUtil.isBlank(accessPlatform)) {
            DefaultConfig defaultConfig = SpringUtil.getBean(DefaultConfig.class);
            if (StrUtil.isBlank(monitorType)) {
                monitorType = defaultConfig.getMonitorType();
            }
            if (StrUtil.isBlank(accessPlatform)) {
                accessPlatform = defaultConfig.getAccessPlatform();
            }
        }
        IVideoBrandEnum videoBrandEnum = IVideoBrandEnum.getOne(monitorType);
        return videoBrandEnum.getMonitorDeviceList(accessPlatform, monitorDeviceDTO.getDeviceCode(), monitorDeviceDTO.getStatus(), pageQuery);
    }

    @Override
    public List<Tree<String>> getNetDeviceTree(String monitorType) {
        IVideoBrandEnum videoBrandEnum = IVideoBrandEnum.getOne(monitorType);
        List<FirstLevelDeviceVO> netFirstLevelDeviceList = videoBrandEnum.getNetFirstLevelDeviceList();
        List<TreeNode<String>> treeNodes = new ArrayList<>();

        for (int i = 0; i < netFirstLevelDeviceList.size(); i++) {
            FirstLevelDeviceVO t = netFirstLevelDeviceList.get(i);
            String code = t.getCode();
            TreeNode<String> treeNode = new TreeNode<>(code, "0", t.getName(), i + 1);
            HashMap<String, Object> disableExtra = new HashMap<>();
            disableExtra.put("monitorType", monitorType);
            disableExtra.put("accessPlatform", t.getAccessPlatform());
            disableExtra.put("disabled", true);
            treeNode.setExtra(disableExtra);
            treeNodes.add(treeNode);
            MonitorDeviceDTO monitorDeviceDTO = new MonitorDeviceDTO();
            monitorDeviceDTO.setDeviceCode(code);
            monitorDeviceDTO.setMonitorType(monitorType);
            monitorDeviceDTO.setAccessPlatform(t.getAccessPlatform());
            PageQuery pageQuery = new PageQuery(999, 1);
            Page<MonitorDeviceVO> monitorDeviceList = videoBrandEnum.getMonitorDeviceList(monitorDeviceDTO.getAccessPlatform(), monitorDeviceDTO.getDeviceCode(), monitorDeviceDTO.getStatus(), pageQuery);
            for (MonitorDeviceVO monitorDeviceVO : monitorDeviceList.getRecords()) {
                String name = Optional.ofNullable(monitorDeviceVO.getName()).orElse(monitorDeviceVO.getId());
                TreeNode<String> childTreeNode = new TreeNode<>(monitorDeviceVO.getId(), code, name, monitorDeviceVO.getChannelNo());
                HashMap<String, Object> extra = (HashMap<String, Object> )disableExtra.clone();
                String status = monitorDeviceVO.getStatus();
                boolean disabled = StrUtil.isBlank(status) || !"1".equals(status);
                extra.put("disabled", disabled);
                extra.put("status", status);
                childTreeNode.setExtra(extra);
                treeNodes.add(childTreeNode);
            }
        }
        return TreeUtil.build(treeNodes, "0");
    }
}